package com.bs.resa.common;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bs.resa.dao.LogMapper;
import com.bs.resa.pojo.AdminUser;
import com.bs.resa.pojo.Log;
import javassist.*;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @Author lin
 * @date 2019/3/30 14:29
 */
//@Aspect
//@Component
public class LogAspect {
    private Logger logger = LoggerFactory.getLogger(LogAspect.class);
    private static SimpleDateFormat df ;//设置日期格式

    @Resource
    RedisUtils redisUtils;

    @Resource
    LogMapper logMapper;

    @Around("execution(* com.bs.resa.web.*.*(..))")
    public Object around(ProceedingJoinPoint pj) throws Throwable {
        df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Log log=new Log();
        Long temp = System.currentTimeMillis();
        JSONObject paramJson = this.printMethodParams(pj,String.valueOf(temp));
        logger.info("请求前："+paramJson.toString());



        Object retVal = pj.proceed();

        JSONObject returnJson = new JSONObject();
        returnJson.put("temp",temp);
        returnJson.put("class_name",paramJson.get("class_name"));
        returnJson.put("method_name",paramJson.get("method_name"));
        returnJson.put("return_name",retVal);
        logger.info("请求后："+returnJson.toString());

        boolean flag=false;
        if(paramJson.get("method_name").equals("projectCreateSum")){
            log.setUserId(((AdminUser)redisUtils.getObject("userinfo")).getId());
            log.setData(df.parse(df.format(new Date())));
            //创建项目
            logger.info("新增项目id:"+redisUtils.get("pid"));
            log.setProjectId(Long.valueOf(redisUtils.get("pid")));
            log.setDesc("创建项目");
            flag=true;
        }
        if(paramJson.get("method_name").equals("projectFileUpload")){
            log.setUserId(((AdminUser)redisUtils.getObject("userinfo")).getId());
            log.setData(df.parse(df.format(new Date())));
            //项目添加附件
            logger.info("项目id:"+redisUtils.get("pid"));
            logger.info("附件id:"+redisUtils.get("fid"));
            log.setProjectId(Long.valueOf(redisUtils.get("pid")));
            log.setDesc("为项目新增附件");
            log.setFileId(Long.valueOf(redisUtils.get("fid")));
            flag=true;
        }


        if(flag){
            logMapper.insertLog(log);
        }
        return retVal;
    }




    /**
     * 打印类method的名称以及参数
     * @param point 切面
     */
    public JSONObject printMethodParams(JoinPoint point, String temp){
        if(point == null){
            return new JSONObject();
        }
        /**
         * Signature 包含了方法名、申明类型以及地址等信息
         */
        String class_name = point.getTarget().getClass().getName();
        String method_name = point.getSignature().getName();
        //重新定义日志
        logger = LoggerFactory.getLogger(point.getTarget().getClass());
        logger.info("class_name = {},temp:"+temp,class_name);
        logger.info("method_name = {},temp:"+temp,method_name);

        JSONObject paramJson = new JSONObject();
        paramJson.put("class_name",class_name);
        paramJson.put("method_name",method_name);
        paramJson.put("temp",temp);
        /**
         * 获取方法的参数值数组。
         */
        Object[] method_args = point.getArgs();

        try {
            //获取方法参数名称
//            String[] paramNames = getFieldsName(class_name, method_name);

//            //打印方法的参数名和参数值
//            String param_name = logParam(paramNames,method_args);
//            paramJson.put("param_name",JSONObject.parse(param_name));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return paramJson;
    }

    /**
     * 使用javassist来获取方法参数名称
     * @param class_name    类名
     * @param method_name   方法名
     * @return
     * @throws Exception
     */
    private String[] getFieldsName(String class_name, String method_name) throws Exception {
        Class<?> clazz = Class.forName(class_name);
        String clazz_name = clazz.getName();
        ClassPool pool = ClassPool.getDefault();
        ClassClassPath classPath = new ClassClassPath(clazz);
        pool.insertClassPath(classPath);

        CtClass ctClass = pool.get(clazz_name);
        CtMethod ctMethod = ctClass.getDeclaredMethod(method_name);
        MethodInfo methodInfo = ctMethod.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
        if(attr == null){
            return null;
        }
        String[] paramsArgsName = new String[ctMethod.getParameterTypes().length];
        // 如果是静态方法，则第一就是参数
        // 如果不是静态方法，则第一个是"this"，然后才是方法的参数
        // 我接口中没有写public修饰词，导致我的数组少一位参数，所以再往后一位，原本应该是   XX ? 0 : 1
        int pos = Modifier.isStatic(ctMethod.getModifiers()) ? 1 : 2;
        for (int i=0;i<paramsArgsName.length;i++){
            paramsArgsName[i] = attr.variableName(i+pos);
        }
        return paramsArgsName;
    }


    /**
     * 判断是否为基本类型：包括String
     * @param clazz clazz
     * @return  true：是;     false：不是
     */
    private boolean isPrimite(Class<?> clazz){
        if (clazz.isPrimitive() || clazz == String.class){
            return true;
        }else {
            return false;
        }
    }


    /**
     * 打印方法参数值  基本类型直接打印，非基本类型需要重写toString方法
     * @param paramsArgsName    方法参数名数组
     * @param paramsArgsValue   方法参数值数组
     */
    private String logParam(String[] paramsArgsName,Object[] paramsArgsValue){
        StringBuffer buffer = new StringBuffer();
        if(ArrayUtils.isEmpty(paramsArgsName) || ArrayUtils.isEmpty(paramsArgsValue)){
            buffer.append("该方法没有参数");
            return buffer.toString();
        }
        for (int i=0;i<paramsArgsName.length;i++){
            //参数名
            String name = paramsArgsName[i];
            //参数值
            Object value = paramsArgsValue[i];
            buffer.append("\""+name+"\":");
            if(isPrimite(value.getClass())){
                buffer.append("\""+value+"\",");
            }else {
                buffer.append(JSON.toJSONString(value)+",");
            }
        }
        return "{"+buffer.toString().substring(0,buffer.toString().length()-1)+"}";
    }

    /**
     * <li>Before       : 在方法执行前进行切面</li>
     * <li>execution    : 定义切面表达式</li>
     * <p>public * com.eparty.ccp.*.impl..*.*(..)
     *      <li>public :匹配所有目标类的public方法，不写则匹配所有访问权限</li>
     *      <li>第一个* :方法返回值类型，*代表所有类型 </li>
     *      <li>第二个* :包路径的通配符</li>
     *      <li>第三个..* :表示impl这个目录下所有的类，包括子目录的类</li>
     *      <li>第四个*(..) : *表示所有任意方法名,..表示任意参数</li>
     * </p>
     * @param
     */
    /*@Before("execution(* com.abc.service.*.many*(..))")
    public void before(JoinPoint point) {
        this.printMethodParams(point);
    }*/




    }
